home *** CD-ROM | disk | FTP | other *** search
/ Graphics Plus / Graphics Plus.iso / amiga / plotting / gnuplot3.lzh / gnuplot / graph3d.c < prev    next >
C/C++ Source or Header  |  1991-09-20  |  52KB  |  1,925 lines

  1. /* GNUPLOT - graph3d.c */
  2. /*
  3.  * Copyright (C) 1986, 1987, 1990, 1991   Thomas Williams, Colin Kelley
  4.  *
  5.  * Permission to use, copy, and distribute this software and its
  6.  * documentation for any purpose with or without fee is hereby granted, 
  7.  * provided that the above copyright notice appear in all copies and 
  8.  * that both that copyright notice and this permission notice appear 
  9.  * in supporting documentation.
  10.  *
  11.  * Permission to modify the software is granted, but not the right to
  12.  * distribute the modified code.  Modifications are to be distributed 
  13.  * as patches to released version.
  14.  *
  15.  * This software is provided "as is" without express or implied warranty.
  16.  *
  17.  *
  18.  * AUTHORS
  19.  *
  20.  *   Original Software:
  21.  *       Gershon Elber and many others.
  22.  *
  23.  * Send your comments or suggestions to 
  24.  *  pixar!info-gnuplot@sun.com.
  25.  * This is a mailing list; to join it send a note to 
  26.  *  pixar!info-gnuplot-request@sun.com.  
  27.  * Send bug reports to
  28.  *  pixar!bug-gnuplot@sun.com.
  29.  */
  30.  
  31. #include <stdio.h>
  32. #include <math.h>
  33. #include <assert.h>
  34. #include <time.h>
  35. #include "plot.h"
  36. #include "setshow.h"
  37.  
  38. extern char *strcpy(),*strncpy(),*strcat(),*ctime(),*tdate;
  39. #ifdef AMIGA_AC_5
  40. extern time_t dated;
  41. #else
  42. #ifdef VMS
  43. extern time_t dated,time();
  44. #else
  45. extern long dated,time();
  46. #endif
  47. #endif
  48.  
  49. static plot3d_impulses();
  50. static plot3d_lines();
  51. static plot3d_points();
  52. static plot3d_dots();
  53. static cntr3d_impulses();
  54. static cntr3d_lines();
  55. static cntr3d_points();
  56. static cntr3d_dots();
  57. static update_extrema_pts();
  58. static draw_parametric_grid();
  59. static draw_non_param_grid();
  60. static draw_bottom_grid();
  61. static draw_3dxtics();
  62. static draw_3dytics();
  63. static draw_3dztics();
  64. static draw_series_3dxtics();
  65. static draw_series_3dytics();
  66. static draw_series_3dztics();
  67. static draw_set_3dxtics();
  68. static draw_set_3dytics();
  69. static draw_set_3dztics();
  70. static xtick();
  71. static ytick();
  72. static ztick();
  73.  
  74. #ifndef max        /* Lattice C has max() in math.h, but shouldn't! */
  75. #define max(a,b) ((a > b) ? a : b)
  76. #endif
  77.  
  78. #ifndef min
  79. #define min(a,b) ((a < b) ? a : b)
  80. #endif
  81.  
  82. #define inrange(z,min,max) ((min<max) ? ((z>=min)&&(z<=max)) : ((z>=max)&&(z<=min)) )
  83.  
  84. #define apx_eq(x,y) (fabs(x-y) < 0.001)
  85. #define abs(x) ((x) >= 0 ? (x) : -(x))
  86. #define sqr(x) ((x) * (x))
  87.  
  88. /* Define the boundary of the plot
  89.  * These are computed at each call to do_plot, and are constant over
  90.  * the period of one do_plot. They actually only change when the term
  91.  * type changes and when the 'set size' factors change. 
  92.  */
  93. static int xleft, xright, ybot, ytop, xmiddle, ymiddle, xscaler, yscaler;
  94.  
  95. /* Boundary and scale factors, in user coordinates */
  96. /* x_min3d, x_max3d, y_min3d, y_max3d, z_min3d, z_max3d are local to this
  97.  * file and are not the same as variables of the same names in other files
  98.  */
  99. static double x_min3d, x_max3d, y_min3d, y_max3d, z_min3d, z_max3d;
  100. static double xscale3d, yscale3d, zscale3d;
  101. static double real_z_min3d, real_z_max3d;
  102. static double min_sy_ox,min_sy_oy; /* obj. coords. for xy tics placement. */
  103. static double min_sx_ox,min_sx_oy; /* obj. coords. for z tics placement. */
  104.  
  105. typedef double transform_matrix[4][4];
  106. static transform_matrix trans_mat;
  107.  
  108. /* (DFK) Watch for cancellation error near zero on axes labels */
  109. #define SIGNIF (0.01)        /* less than one hundredth of a tic mark */
  110. #define CheckZero(x,tic) (fabs(x) < ((tic) * SIGNIF) ? 0.0 : (x))
  111. #define NearlyEqual(x,y,tic) (fabs((x)-(y)) < ((tic) * SIGNIF))
  112.  
  113. /* And the functions to map from user to terminal coordinates */
  114. #define map_x(x) (int)(x+0.5) /* maps floating point x to screen */ 
  115. #define map_y(y) (int)(y+0.5)    /* same for y */
  116.  
  117. /* And the functions to map from user 3D space into normalized -1..1 */
  118. #define map_x3d(x) ((x-x_min3d)*xscale3d-1.0)
  119. #define map_y3d(y) ((y-y_min3d)*yscale3d-1.0)
  120. #define map_z3d(z) ((z-z_min3d)*zscale3d-1.0)
  121.  
  122. static mat_unit(mat)
  123. transform_matrix mat;
  124. {
  125.     int i, j;
  126.  
  127.     for (i = 0; i < 4; i++) for (j = 0; j < 4; j++)
  128.     if (i == j)
  129.         mat[i][j] = 1.0;
  130.     else
  131.         mat[i][j] = 0.0;
  132. }
  133.  
  134. static mat_trans(tx, ty, tz, mat)
  135. double tx, ty, tz;
  136. transform_matrix mat;
  137. {
  138.      mat_unit(mat);                                 /* Make it unit matrix. */
  139.      mat[3][0] = tx;
  140.      mat[3][1] = ty;
  141.      mat[3][2] = tz;
  142. }
  143.  
  144. static mat_scale(sx, sy, sz, mat)
  145. double sx, sy, sz;
  146. transform_matrix mat;
  147. {
  148.      mat_unit(mat);                                 /* Make it unit matrix. */
  149.      mat[0][0] = sx;
  150.      mat[1][1] = sy;
  151.      mat[2][2] = sz;
  152. }
  153.  
  154. static mat_rot_x(teta, mat)
  155. double teta;
  156. transform_matrix mat;
  157. {
  158.     double cos_teta, sin_teta;
  159.  
  160.     teta *= Pi / 180.0;
  161.     cos_teta = cos(teta);
  162.     sin_teta = sin(teta);
  163.  
  164.     mat_unit(mat);                                  /* Make it unit matrix. */
  165.     mat[1][1] = cos_teta;
  166.     mat[1][2] = -sin_teta;
  167.     mat[2][1] = sin_teta;
  168.     mat[2][2] = cos_teta;
  169. }
  170.  
  171. static mat_rot_y(teta, mat)
  172. double teta;
  173. transform_matrix mat;
  174. {
  175.     double cos_teta, sin_teta;
  176.  
  177.     teta *= Pi / 180.0;
  178.     cos_teta = cos(teta);
  179.     sin_teta = sin(teta);
  180.  
  181.     mat_unit(mat);                                  /* Make it unit matrix. */
  182.     mat[0][0] = cos_teta;
  183.     mat[0][2] = -sin_teta;
  184.     mat[2][0] = sin_teta;
  185.     mat[2][2] = cos_teta;
  186. }
  187.  
  188. static mat_rot_z(teta, mat)
  189. double teta;
  190. transform_matrix mat;
  191. {
  192.     double cos_teta, sin_teta;
  193.  
  194.     teta *= Pi / 180.0;
  195.     cos_teta = cos(teta);
  196.     sin_teta = sin(teta);
  197.  
  198.     mat_unit(mat);                                  /* Make it unit matrix. */
  199.     mat[0][0] = cos_teta;
  200.     mat[0][1] = -sin_teta;
  201.     mat[1][0] = sin_teta;
  202.     mat[1][1] = cos_teta;
  203. }
  204.  
  205. /* Multiply two transform_matrix. Result can be one of two operands. */
  206. void mat_mult(mat_res, mat1, mat2)
  207. transform_matrix mat_res, mat1, mat2;
  208. {
  209.     int i, j, k;
  210.     transform_matrix mat_res_temp;
  211.  
  212.     for (i = 0; i < 4; i++) for (j = 0; j < 4; j++) {
  213.         mat_res_temp[i][j] = 0;
  214.         for (k = 0; k < 4; k++) mat_res_temp[i][j] += mat1[i][k] * mat2[k][j];
  215.     }
  216.     for (i = 0; i < 4; i++) for (j = 0; j < 4; j++)
  217.     mat_res[i][j] = mat_res_temp[i][j];
  218. }
  219.  
  220. /* And the functions to map from user 3D space to terminal coordinates */
  221. static int map3d_xy(x, y, z, xt, yt)
  222. double x, y, z;
  223. int *xt, *yt;
  224. {
  225.     int i,j;
  226.     double v[4], res[4],             /* Homogeneous coords. vectors. */
  227.     w = trans_mat[3][3];
  228.  
  229.     v[0] = map_x3d(x); /* Normalize object space to -1..1 */
  230.     v[1] = map_y3d(y);
  231.     v[2] = map_z3d(z);
  232.     v[3] = 1.0;
  233.  
  234.     for (i = 0; i < 2; i++) {                 /* Dont use the third axes (z). */
  235.         res[i] = trans_mat[3][i];     /* Initiate it with the weight factor. */
  236.         for (j = 0; j < 3; j++) res[i] += v[j] * trans_mat[j][i];
  237.     }
  238.  
  239.     for (i = 0; i < 3; i++) w += v[i] * trans_mat[i][3];
  240.     if (w == 0) w = 1e-5;
  241.  
  242.     *xt = ((int) (res[0] * xscaler / w)) + xmiddle;
  243.     *yt = ((int) (res[1] * yscaler / w)) + ymiddle;
  244. }
  245.  
  246. /* Test a single point to be within the xleft,xright,ybot,ytop bbox.
  247.  * Sets the returned integers 4 l.s.b. as follows:
  248.  * bit 0 if to the left of xleft.
  249.  * bit 1 if to the right of xright.
  250.  * bit 2 if above of ytop.
  251.  * bit 3 if below of ybot.
  252.  * 0 is returned if inside.
  253.  */
  254. static int clip_point(x, y)
  255. int x, y;
  256. {
  257.     int ret_val = 0;
  258.  
  259.     if (x < xleft) ret_val |= 0x01;
  260.     if (x > xright) ret_val |= 0x02;
  261.     if (y < ybot) ret_val |= 0x04;
  262.     if (y > ytop) ret_val |= 0x08;
  263.  
  264.     return ret_val;
  265. }
  266.  
  267. /* Clip the given line to drawing coords defined as xleft,xright,ybot,ytop.
  268.  *   This routine uses the cohen & sutherland bit mapping for fast clipping -
  269.  * see "Principles of Interactive Computer Graphics" Newman & Sproull page 65.
  270.  */
  271. static void draw_clip_line(x1, y1, x2, y2)
  272. int x1, y1, x2, y2;
  273. {
  274.     int x, y, dx, dy, x_intr[2], y_intr[2], count, pos1, pos2;
  275.     register struct termentry *t = &term_tbl[term];
  276.  
  277.     pos1 = clip_point(x1, y1);
  278.     pos2 = clip_point(x2, y2);
  279.     if (pos1 || pos2) {
  280.     if (pos1 & pos2) return;          /* segment is totally out. */
  281.  
  282.     /* Here part of the segment MAY be inside. test the intersection
  283.      * of this segment with the 4 boundaries for hopefully 2 intersections
  284.      * in. If non found segment is totaly out.
  285.      */
  286.     count = 0;
  287.     dx = x2 - x1;
  288.     dy = y2 - y1;
  289.  
  290.     /* Find intersections with the x parallel bbox lines: */
  291.     if (dy != 0) {
  292.         x = (ybot - y2) * dx / dy + x2;        /* Test for ybot boundary. */
  293.         if (x >= xleft && x <= xright) {
  294.         x_intr[count] = x;
  295.         y_intr[count++] = ybot;
  296.         }
  297.         x = (ytop - y2) * dx / dy + x2;        /* Test for ytop boundary. */
  298.         if (x >= xleft && x <= xright) {
  299.         x_intr[count] = x;
  300.         y_intr[count++] = ytop;
  301.         }
  302.     }
  303.  
  304.     /* Find intersections with the y parallel bbox lines: */
  305.     if (dx != 0) {
  306.         y = (xleft - x2) * dy / dx + y2;      /* Test for xleft boundary. */
  307.         if (y >= ybot && y <= ytop) {
  308.         x_intr[count] = xleft;
  309.         y_intr[count++] = y;
  310.         }
  311.         y = (xright - x2) * dy / dx + y2;    /* Test for xright boundary. */
  312.         if (y >= ybot && y <= ytop) {
  313.         x_intr[count] = xright;
  314.         y_intr[count++] = y;
  315.         }
  316.     }
  317.  
  318.     if (count == 2) {
  319.         int x_max, x_min, y_max, y_min;
  320.  
  321.         x_min = min(x1, x2);
  322.         x_max = max(x1, x2);
  323.         y_min = min(y1, y2);
  324.         y_max = max(y1, y2);
  325.  
  326.         if (pos1 && pos2) {               /* Both were out - update both */
  327.         x1 = x_intr[0];
  328.         y1 = y_intr[0];
  329.         x2 = x_intr[1];
  330.         y2 = y_intr[1];
  331.         }
  332.         else if (pos1) {           /* Only x1/y1 was out - update only it */
  333.         if (dx * (x2 - x_intr[0]) + dy * (y2 - y_intr[0]) > 0) {
  334.             x1 = x_intr[0];
  335.             y1 = y_intr[0];
  336.         }
  337.         else {
  338.             x1 = x_intr[1];
  339.             y1 = y_intr[1];
  340.         }
  341.         }
  342.         else {                      /* Only x2/y2 was out - update only it */
  343.         if (dx * (x_intr[0] - x1) + dy * (y_intr[0] - x1) > 0) {
  344.             x2 = x_intr[0];
  345.             y2 = y_intr[0];
  346.         }
  347.         else {
  348.             x2 = x_intr[1];
  349.             y2 = y_intr[1];
  350.         }
  351.         }
  352.  
  353.         if (x1 < x_min || x1 > x_max ||
  354.         x2 < x_min || x2 > x_max ||
  355.         y1 < y_min || y1 > y_max ||
  356.         y2 < y_min || y2 > y_max) return;
  357.     }
  358.     else
  359.         return;
  360.     }
  361.  
  362.     (*t->move)(x1,y1);
  363.     (*t->vector)(x2,y2);
  364. }
  365.  
  366. /* Two routine to emulate move/vector sequence using line drawing routine. */
  367. static int move_pos_x, move_pos_y;
  368.  
  369. static void clip_move(x,y)
  370. int x,y;
  371. {
  372.     move_pos_x = x;
  373.     move_pos_y = y;
  374. }
  375.  
  376. static void clip_vector(x,y)
  377. int x,y;
  378. {
  379.     draw_clip_line(move_pos_x,move_pos_y, x, y);
  380.     move_pos_x = x;
  381.     move_pos_y = y;
  382. }
  383.  
  384. /* And text clipping routine. */
  385. static void clip_put_text(x, y, str)
  386. int x,y;
  387. char *str;
  388. {
  389.     register struct termentry *t = &term_tbl[term];
  390.  
  391.     if (clip_point(x, y)) return;
  392.  
  393.     (*t->put_text)(x,y,str);
  394. }
  395.  
  396. /* (DFK) For some reason, the Sun386i compiler screws up with the CheckLog 
  397.  * macro, so I write it as a function on that machine.
  398.  */
  399. #ifndef sun386
  400. /* (DFK) Use 10^x if logscale is in effect, else x */
  401. #define CheckLog(log, x) ((log) ? pow(10., (x)) : (x))
  402. #else
  403. static double
  404. CheckLog(log, x)
  405.      BOOLEAN log;
  406.      double x;
  407. {
  408.   if (log)
  409.     return(pow(10., x));
  410.   else
  411.     return(x);
  412. }
  413. #endif /* sun386 */
  414.  
  415. static double
  416. LogScale(coord, islog, what, axis)
  417.     double coord;            /* the value */
  418.     BOOLEAN islog;            /* is this axis in logscale? */
  419.     char *what;            /* what is the coord for? */
  420.     char *axis;            /* which axis is this for ("x" or "y")? */
  421. {
  422.     if (islog) {
  423.        if (coord <= 0.0) {
  424.           char errbuf[100];        /* place to write error message */
  425.         (void) sprintf(errbuf,"%s has %s coord of %g; must be above 0 for log scale!",
  426.                 what, axis, coord);
  427.           (*term_tbl[term].text)();
  428.           (void) fflush(outfile);
  429.           int_error(errbuf, NO_CARET);
  430.        } else
  431.         return(log10(coord));
  432.     }
  433.     return(coord);
  434. }
  435.  
  436. /* borders of plotting area */
  437. /* computed once on every call to do_plot */
  438. static boundary3d(scaling)
  439.     BOOLEAN scaling;        /* TRUE if terminal is doing the scaling */
  440. {
  441.     register struct termentry *t = &term_tbl[term];
  442.     xleft = (t->h_char)*2 + (t->h_tic);
  443.     xright = (scaling ? 1 : xsize) * (t->xmax) - (t->h_char)*2 - (t->h_tic);
  444.     ybot = (t->v_char)*5/2 + 1;
  445.     ytop = (scaling ? 1 : ysize) * (t->ymax) - (t->v_char)*5/2 - 1;
  446.     xmiddle = (xright + xleft) / 2;
  447.     ymiddle = (ytop + ybot) / 2;
  448.     xscaler = (xright - xleft) / 2;
  449.     yscaler = (ytop - ybot) / 2;
  450. }
  451.  
  452. static double dbl_raise(x,y)
  453. double x;
  454. int y;
  455. {
  456. register int i;
  457. double val;
  458.  
  459.     val = 1.0;
  460.     for (i=0; i < abs(y); i++)
  461.         val *= x;
  462.     if (y < 0 ) return (1.0/val);
  463.     return(val);
  464. }
  465.  
  466.  
  467. static double make_3dtics(tmin,tmax,axis,logscale)
  468. double tmin,tmax;
  469. int axis;
  470. BOOLEAN logscale;
  471. {
  472. int len,x1,y1,x2,y2;
  473. register double xr,xnorm,tics,tic,l10;
  474.  
  475.     xr = fabs(tmin-tmax);
  476.  
  477.     /* Compute length of axis in screen space coords. */
  478.     switch (axis) {
  479.         case 'x':
  480.             map3d_xy(tmin,0.0,0.0,&x1,&y1);
  481.             map3d_xy(tmax,0.0,0.0,&x2,&y2);
  482.             break;
  483.         case 'y':
  484.             map3d_xy(0.0,tmin,0.0,&x1,&y1);
  485.             map3d_xy(0.0,tmax,0.0,&x2,&y2);
  486.             break;
  487.         case 'z':
  488.             map3d_xy(0.0,0.0,tmin,&x1,&y1);
  489.             map3d_xy(0.0,0.0,tmax,&x2,&y2);
  490.             break;
  491.     }
  492.  
  493.     if (((long) (x1-x2))*(x1-x2) + ((long) (y1-y2))*(y1-y2) <
  494.         sqr(3L * term_tbl[term].h_char))
  495.         return -1.0;                              /* No tics! */
  496.  
  497.     l10 = log10(xr);
  498.     if (logscale) {
  499.         tic = dbl_raise(10.0,(l10 >= 0.0 ) ? (int)l10 : ((int)l10-1));
  500.         if (tic < 1.0)
  501.             tic = 1.0;
  502.     } else {
  503.         xnorm = pow(10.0,l10-(double)((l10 >= 0.0 ) ? (int)l10 : ((int)l10-1)));
  504.         if (xnorm <= 5)
  505.             tics = 0.5;
  506.         else tics = 1.0;
  507.         tic = tics * dbl_raise(10.0,(l10 >= 0.0 ) ? (int)l10 : ((int)l10-1));
  508.     }
  509.     return(tic);
  510. }
  511.  
  512. do_3dplot(plots, pcount, min_x, max_x, min_y, max_y, min_z, max_z)
  513. struct surface_points *plots;
  514. int pcount;            /* count of plots in linked list */
  515. double min_x, max_x;
  516. double min_y, max_y;
  517. double min_z, max_z;
  518. {
  519. register struct termentry *t = &term_tbl[term];
  520. register int surface, xaxis_y, yaxis_x;
  521. register struct surface_points *this_plot;
  522. int xl, yl;
  523.             /* only a Pyramid would have this many registers! */
  524. double xtemp, ytemp, ztemp, temp;
  525. struct text_label *this_label;
  526. struct arrow_def *this_arrow;
  527. BOOLEAN scaling;
  528. transform_matrix mat;
  529.  
  530. /* Initiate transformation matrix using the global view variables. */
  531.     mat_rot_z(surface_rot_z, trans_mat);
  532.     mat_rot_x(surface_rot_x, mat);
  533.     mat_mult(trans_mat, trans_mat, mat);
  534.     mat_scale(surface_scale / 2.0, surface_scale / 2.0, surface_scale / 2.0, mat);
  535.     mat_mult(trans_mat, trans_mat, mat);
  536.  
  537. /* modify min_z/max_z so it will zscale properly. */
  538.     ztemp = (max_z - min_z) / (2.0 * surface_zscale);
  539.     temp = (max_z + min_z) / 2.0;
  540.     min_z = temp - ztemp;
  541.     max_z = temp + ztemp;
  542.  
  543. /* store these in variables global to this file */
  544. /* otherwise, we have to pass them around a lot */
  545.     x_min3d = min_x;
  546.     x_max3d = max_x;
  547.     y_min3d = min_y;
  548.     y_max3d = max_y;
  549.     z_min3d = min_z;
  550.     z_max3d = max_z;
  551.  
  552.     if (polar)
  553.     int_error("Can not splot in polar coordinate system.", NO_CARET);
  554.  
  555.     if (z_min3d == VERYLARGE || z_max3d == -VERYLARGE ||
  556.     x_min3d == VERYLARGE || x_max3d == -VERYLARGE ||
  557.     y_min3d == VERYLARGE || y_max3d == -VERYLARGE)
  558.         int_error("all points undefined!", NO_CARET);
  559.  
  560.     /* If we are to draw the bottom grid make sure zmin is updated properly. */
  561.     if (xtics || ytics || grid)
  562.     z_min3d -= (max_z - min_z) * ticslevel;
  563.  
  564. /*  This used be x_max3d == x_min3d, but that caused an infinite loop once. */
  565.     if (fabs(x_max3d - x_min3d) < zero)
  566.     int_error("x_min3d should not equal x_max3d!",NO_CARET);
  567.     if (fabs(y_max3d - y_min3d) < zero)
  568.     int_error("y_min3d should not equal y_max3d!",NO_CARET);
  569.     if (fabs(z_max3d - z_min3d) < zero)
  570.     int_error("z_min3d should not equal z_max3d!",NO_CARET);
  571.  
  572. /* INITIALIZE TERMINAL */
  573.     if (!term_init) {
  574.     (*t->init)();
  575.     term_init = TRUE;
  576.     }
  577.     screen_ok = FALSE;
  578.     scaling = (*t->scale)(xsize, ysize);
  579.     (*t->graphics)();
  580.  
  581.     /* now compute boundary for plot (xleft, xright, ytop, ybot) */
  582.     boundary3d(scaling);
  583.  
  584. /* SCALE FACTORS */
  585.     zscale3d = 2.0/(z_max3d - z_min3d);
  586.     yscale3d = 2.0/(y_max3d - y_min3d);
  587.     xscale3d = 2.0/(x_max3d - x_min3d);
  588.  
  589.     (*t->linetype)(-2); /* border linetype */
  590. /* PLACE TITLE */
  591.     if (*title != 0) {
  592.         int x, y;
  593.  
  594.         x = title_xoffset * t->h_char;
  595.         y = title_yoffset * t->v_char;
  596.  
  597.         if ((*t->justify_text)(CENTRE)) 
  598.             (*t->put_text)(x+(xleft+xright)/2, 
  599.                        y+ytop+(t->v_char), title);
  600.         else
  601.             (*t->put_text)(x+(xleft+xright)/2 - strlen(title)*(t->h_char)/2,
  602.                        y+ytop+(t->v_char), title);
  603.     }
  604.  
  605. /* PLACE TIMEDATE */
  606.     if (timedate) {
  607.         int x, y;
  608.  
  609.         x = time_xoffset * t->h_char;
  610.         y = time_yoffset * t->v_char;
  611.         dated = time( (long *) 0);
  612.         tdate = ctime( &dated);
  613.         tdate[24]='\0';
  614.         if ((*t->text_angle)(1)) {
  615.             if ((*t->justify_text)(CENTRE)) {
  616.                 (*t->put_text)(x+(t->v_char),
  617.                          y+ybot+4*(t->v_char), tdate);
  618.             }
  619.             else {
  620.                 (*t->put_text)(x+(t->v_char),
  621.                          y+ybot+4*(t->v_char)-(t->h_char)*strlen(ylabel)/2, 
  622.                          tdate);
  623.             }
  624.         }
  625.         else {
  626.             (void)(*t->justify_text)(LEFT);
  627.             (*t->put_text)(x,
  628.                          y+ybot-3*(t->v_char), tdate);
  629.         }
  630.         (void)(*t->text_angle)(0);
  631.     }
  632.  
  633. /* PLACE LABELS */
  634.     for (this_label = first_label; this_label!=NULL;
  635.             this_label=this_label->next ) {
  636.         int x,y;
  637.  
  638.         xtemp = LogScale(this_label->x, log_x, "label", "x");
  639.         ytemp = LogScale(this_label->y, log_y, "label", "y");
  640.         ztemp = LogScale(this_label->z, log_z, "label", "z");
  641.             map3d_xy(xtemp,ytemp,ztemp, &x, &y);
  642.  
  643.         if ((*t->justify_text)(this_label->pos)) {
  644.             (*t->put_text)(x,y,this_label->text);
  645.         }
  646.         else {
  647.             switch(this_label->pos) {
  648.                 case  LEFT:
  649.                     (*t->put_text)(x,y,this_label->text);
  650.                     break;
  651.                 case CENTRE:
  652.                     (*t->put_text)(x -
  653.                         (t->h_char)*strlen(this_label->text)/2,
  654.                         y, this_label->text);
  655.                     break;
  656.                 case RIGHT:
  657.                     (*t->put_text)(x -
  658.                         (t->h_char)*strlen(this_label->text),
  659.                         y, this_label->text);
  660.                     break;
  661.             }
  662.          }
  663.      }
  664.  
  665. /* PLACE ARROWS */
  666.     (*t->linetype)(0);    /* arrow line type */
  667.     for (this_arrow = first_arrow; this_arrow!=NULL;
  668.         this_arrow = this_arrow->next ) {
  669.     int sx,sy,ex,ey;
  670.  
  671.     xtemp = LogScale(this_arrow->sx, log_x, "arrow", "x");
  672.     ytemp = LogScale(this_arrow->sy, log_y, "arrow", "y");
  673.     ztemp = LogScale(this_arrow->sz, log_y, "arrow", "z");
  674.     map3d_xy(xtemp,ytemp,ztemp, &sx, &sy);
  675.  
  676.     xtemp = LogScale(this_arrow->ex, log_x, "arrow", "x");
  677.     ytemp = LogScale(this_arrow->ey, log_y, "arrow", "y");
  678.     ztemp = LogScale(this_arrow->ez, log_y, "arrow", "z");
  679.     map3d_xy(xtemp,ytemp,ztemp, &ex, &ey);
  680.  
  681.     (*t->arrow)(sx, sy, ex, ey, this_arrow->head);
  682.     }
  683.  
  684.  
  685. /* DRAW SURFACES AND CONTOURS */
  686.     real_z_min3d = min_z;
  687.     real_z_max3d = max_z;
  688.     if (key == -1) {
  689.         xl = xright  - (t->h_tic) - (t->h_char)*5;
  690.         yl = ytop - (t->v_tic) - (t->v_char);
  691.     }
  692.     if (key == 1) {
  693.         xtemp = LogScale(key_x, log_x, "key", "x");
  694.         ytemp = LogScale(key_y, log_y, "key", "y");
  695.         ztemp = LogScale(key_z, log_z, "key", "z");
  696.         map3d_xy(xtemp,ytemp,ztemp, &xl, &yl);
  697.     }
  698.  
  699.     this_plot = plots;
  700.     for (surface = 0;
  701.          surface < pcount;
  702.          this_plot = this_plot->next_sp, surface++) {
  703.         (*t->linetype)(this_plot->line_type+1);
  704.  
  705.         if (draw_contour && this_plot->contours != NULL) {
  706.             struct gnuplot_contours *cntrs = this_plot->contours;
  707.  
  708.             if (key != 0) {
  709.                 if ((*t->justify_text)(RIGHT)) {
  710.                     clip_put_text(xl,
  711.                         yl,this_plot->title);
  712.                 }
  713.                 else {
  714.                     if (inrange(xl-(t->h_char)*strlen(this_plot->title), 
  715.                              xleft, xright))
  716.                      clip_put_text(xl-(t->h_char)*strlen(this_plot->title),
  717.                                  yl,this_plot->title);
  718.                 }
  719.                 switch(this_plot->plot_style) {
  720.                     case IMPULSES:
  721.                         clip_move(xl+(t->h_char),yl);
  722.                         clip_vector(xl+4*(t->h_char),yl);
  723.                         break;
  724.                     case LINES:
  725.                         clip_move(xl+(int)(t->h_char),yl);
  726.                         clip_vector(xl+(int)(4*(t->h_char)),yl);
  727.                         break;
  728.                     case ERRORBARS: /* ignored; treat like points */
  729.                     case POINTS:
  730.                         if (!clip_point(xl+2*(t->h_char),yl)) {
  731.                              (*t->point)(xl+2*(t->h_char),yl,
  732.                                     this_plot->point_type);
  733.                         }
  734.                         break;
  735.                     case LINESPOINTS:
  736.                         clip_move(xl+(int)(t->h_char),yl);
  737.                         clip_vector(xl+(int)(4*(t->h_char)),yl);
  738.                         break;
  739.                     case DOTS:
  740.                         if (!clip_point(xl+2*(t->h_char),yl)) {
  741.                              (*t->point)(xl+2*(t->h_char),yl, -1);
  742.                         }
  743.                         break;
  744.                 }
  745.             }
  746.  
  747.             while (cntrs) {
  748.                 switch(this_plot->plot_style) {
  749.                     case IMPULSES:
  750.                            cntr3d_impulses(cntrs, this_plot);
  751.                         break;
  752.                     case LINES:
  753.                         cntr3d_lines(cntrs);
  754.                         break;
  755.                     case ERRORBARS: /* ignored; treat like points */
  756.                     case POINTS:
  757.                         cntr3d_points(cntrs, this_plot);
  758.                         break;
  759.                     case LINESPOINTS:
  760.                         cntr3d_lines(cntrs);
  761.                         cntr3d_points(cntrs, this_plot);
  762.                         break;
  763.                     case DOTS:
  764.                         cntr3d_dots(cntrs);
  765.                         break;
  766.                 }
  767.                 cntrs = cntrs->next;
  768.             }
  769.             if (key != 0) yl = yl - (t->v_char);
  770.         }
  771.  
  772.         if ( surface == 0 )
  773.             draw_bottom_grid(this_plot,real_z_min3d,real_z_max3d);
  774.  
  775.         if (!draw_surface) continue;
  776.         (*t->linetype)(this_plot->line_type);
  777.  
  778.         if (key != 0) {
  779.             if ((*t->justify_text)(RIGHT)) {
  780.                 clip_put_text(xl,
  781.                     yl,this_plot->title);
  782.             }
  783.             else {
  784.                 if (inrange(xl-(t->h_char)*strlen(this_plot->title), 
  785.                          xleft, xright))
  786.                  clip_put_text(xl-(t->h_char)*strlen(this_plot->title),
  787.                              yl,this_plot->title);
  788.             }
  789.         }
  790.  
  791.         switch(this_plot->plot_style) {
  792.             case IMPULSES: {
  793.                if (key != 0) {
  794.                   clip_move(xl+(t->h_char),yl);
  795.                   clip_vector(xl+4*(t->h_char),yl);
  796.                }
  797.                plot3d_impulses(this_plot);
  798.                break;
  799.             }
  800.             case LINES: {
  801.                if (key != 0) {
  802.                   clip_move(xl+(int)(t->h_char),yl);
  803.                   clip_vector(xl+(int)(4*(t->h_char)),yl);
  804.                }
  805.                plot3d_lines(this_plot);
  806.                break;
  807.             }
  808.             case ERRORBARS:    /* ignored; treat like points */
  809.             case POINTS: {
  810.                if (key != 0 && !clip_point(xl+2*(t->h_char),yl)) {
  811.                   (*t->point)(xl+2*(t->h_char),yl,
  812.                             this_plot->point_type);
  813.                }
  814.                plot3d_points(this_plot);
  815.                break;
  816.             }
  817.             case LINESPOINTS: {
  818.                /* put lines */
  819.                if (key != 0) {
  820.                   clip_move(xl+(t->h_char),yl);
  821.                   clip_vector(xl+4*(t->h_char),yl);
  822.                }
  823.                plot3d_lines(this_plot);
  824.  
  825.                /* put points */
  826.                if (key != 0 && !clip_point(xl+2*(t->h_char),yl)) {
  827.                   (*t->point)(xl+2*(t->h_char),yl,
  828.                             this_plot->point_type);
  829.                }
  830.                plot3d_points(this_plot);
  831.                break;
  832.             }
  833.             case DOTS: {
  834.                if (key != 0 && !clip_point(xl+2*(t->h_char),yl)) {
  835.                   (*t->point)(xl+2*(t->h_char),yl, -1);
  836.                }
  837.                plot3d_dots(this_plot);
  838.                break;
  839.             }
  840.         }
  841.         yl = yl - (t->v_char);
  842.     }
  843.     (*t->text)();
  844.     (void) fflush(outfile);
  845. }
  846.  
  847. /* plot3d_impulses:
  848.  * Plot the surfaces in IMPULSES style
  849.  */
  850. static plot3d_impulses(plot)
  851.     struct surface_points *plot;
  852. {
  853.     int i;                /* point index */
  854.     int x,y,x0,y0;            /* point in terminal coordinates */
  855.     struct termentry *t = &term_tbl[term];
  856.     struct iso_curve *icrvs = plot->iso_crvs;
  857.  
  858.     while ( icrvs ) {
  859.     struct coordinate *points = icrvs->points;
  860.  
  861.     for (i = 0; i < icrvs->p_count; i++) {
  862.         if (real_z_max3d<points[i].z)
  863.         real_z_max3d=points[i].z;
  864.         if (real_z_min3d>points[i].z)
  865.         real_z_min3d=points[i].z;
  866.  
  867.         map3d_xy(points[i].x, points[i].y, points[i].z, &x, &y);
  868.         map3d_xy(points[i].x, points[i].y, z_min3d, &x0, &y0);
  869.  
  870.         clip_move(x0,y0);
  871.         clip_vector(x,y);
  872.     }
  873.  
  874.     icrvs = icrvs->next;
  875.     }
  876. }
  877.  
  878. /* plot3d_lines:
  879.  * Plot the surfaces in LINES style
  880.  */
  881. static plot3d_lines(plot)
  882.     struct surface_points *plot;
  883. {
  884.     int i;
  885.     int x,y;                /* point in terminal coordinates */
  886.     struct termentry *t = &term_tbl[term];
  887.     struct iso_curve *icrvs = plot->iso_crvs;
  888.  
  889.     while ( icrvs ) {
  890.     struct coordinate *points = icrvs->points;
  891.  
  892.     for (i = 0; i < icrvs->p_count; i++) {
  893.         if (real_z_max3d<points[i].z)
  894.         real_z_max3d=points[i].z;
  895.         if (real_z_min3d>points[i].z)
  896.         real_z_min3d=points[i].z;
  897.  
  898.         map3d_xy(points[i].x, points[i].y, points[i].z, &x, &y);
  899.  
  900.         if (i > 0)
  901.         clip_vector(x,y);
  902.         else
  903.         clip_move(x,y);
  904.     }
  905.  
  906.     icrvs = icrvs->next;
  907.     }
  908. }
  909.  
  910. /* plot3d_points:
  911.  * Plot the surfaces in POINTS style
  912.  */
  913. static plot3d_points(plot)
  914.     struct surface_points *plot;
  915. {
  916.     int i,x,y;
  917.     struct termentry *t = &term_tbl[term];
  918.     struct iso_curve *icrvs = plot->iso_crvs;
  919.  
  920.     while ( icrvs ) {
  921.     struct coordinate *points = icrvs->points;
  922.  
  923.     for (i = 0; i < icrvs->p_count; i++) {
  924.         if (real_z_max3d<points[i].z)
  925.         real_z_max3d=points[i].z;
  926.         if (real_z_min3d>points[i].z)
  927.         real_z_min3d=points[i].z;
  928.  
  929.         map3d_xy(points[i].x, points[i].y, points[i].z, &x, &y);
  930.  
  931.         if (!clip_point(x,y))
  932.         (*t->point)(x,y, plot->point_type);
  933.     }
  934.  
  935.     icrvs = icrvs->next;
  936.     }
  937. }
  938.  
  939. /* plot3d_dots:
  940.  * Plot the surfaces in DOTS style
  941.  */
  942. static plot3d_dots(plot)
  943.     struct surface_points *plot;
  944. {
  945.     int i,x,y;
  946.     struct termentry *t = &term_tbl[term];
  947.     struct iso_curve *icrvs = plot->iso_crvs;
  948.  
  949.     while ( icrvs ) {
  950.     struct coordinate *points = icrvs->points;
  951.  
  952.         for (i = 0; i < icrvs->p_count; i++) {
  953.         if (real_z_max3d<points[i].z)
  954.         real_z_max3d=points[i].z;
  955.         if (real_z_min3d>points[i].z)
  956.             real_z_min3d=points[i].z;
  957.  
  958.             map3d_xy(points[i].x, points[i].y, points[i].z, &x, &y);
  959.  
  960.             if (!clip_point(x,y))
  961.         (*t->point)(x,y, -1);
  962.         }
  963.  
  964.     icrvs = icrvs->next;
  965.     }
  966. }
  967.  
  968. /* cntr3d_impulses:
  969.  * Plot a surface contour in IMPULSES style
  970.  */
  971. static cntr3d_impulses(cntr, plot)
  972.     struct gnuplot_contours *cntr;
  973.     struct surface_points *plot;
  974. {
  975.     int i,j,k;                /* point index */
  976.     int x,y,x0,y0;            /* point in terminal coordinates */
  977.     struct termentry *t = &term_tbl[term];
  978.  
  979.     if (draw_contour == CONTOUR_SRF || draw_contour == CONTOUR_BOTH) {
  980.     for (i = 0; i < cntr->num_pts; i++) {
  981.         if (real_z_max3d<cntr->coords[i].z)
  982.         real_z_max3d=cntr->coords[i].z;
  983.         if (real_z_min3d>cntr->coords[i].z)
  984.         real_z_min3d=cntr->coords[i].z;
  985.  
  986.         map3d_xy(cntr->coords[i].x, cntr->coords[i].y, cntr->coords[i].z,
  987.              &x, &y);
  988.         map3d_xy(cntr->coords[i].x, cntr->coords[i].y, z_min3d,
  989.              &x0, &y0);
  990.  
  991.         clip_move(x0,y0);
  992.         clip_vector(x,y);
  993.     }
  994.     }
  995.     else
  996.     cntr3d_points(cntr, plot);   /* Must be on base grid, so do points. */
  997. }
  998.  
  999. /* cntr3d_lines:
  1000.  * Plot a surface contour in LINES style
  1001.  */
  1002. static cntr3d_lines(cntr)
  1003.     struct gnuplot_contours *cntr;
  1004. {
  1005.     int i,j,k;                /* point index */
  1006.     int x,y;                /* point in terminal coordinates */
  1007.     struct termentry *t = &term_tbl[term];
  1008.  
  1009.     if (draw_contour == CONTOUR_SRF || draw_contour == CONTOUR_BOTH) {
  1010.     for (i = 0; i < cntr->num_pts; i++) {
  1011.         if (real_z_max3d<cntr->coords[i].z)
  1012.         real_z_max3d=cntr->coords[i].z;
  1013.         if (real_z_min3d>cntr->coords[i].z)
  1014.         real_z_min3d=cntr->coords[i].z;
  1015.  
  1016.             map3d_xy(cntr->coords[i].x, cntr->coords[i].y, cntr->coords[i].z,
  1017.                  &x, &y);
  1018.  
  1019.             if (i > 0)
  1020.             clip_vector(x,y);
  1021.         else
  1022.         clip_move(x,y);
  1023.         }
  1024.     }
  1025.  
  1026.     if (draw_contour == CONTOUR_BASE || draw_contour == CONTOUR_BOTH) {
  1027.     for (i = 0; i < cntr->num_pts; i++) {
  1028.         if (real_z_max3d<cntr->coords[i].z)
  1029.         real_z_max3d=cntr->coords[i].z;
  1030.         if (real_z_min3d>cntr->coords[i].z)
  1031.         real_z_min3d=cntr->coords[i].z;
  1032.  
  1033.             map3d_xy(cntr->coords[i].x, cntr->coords[i].y, z_min3d,
  1034.                  &x, &y);
  1035.  
  1036.             if (i > 0)
  1037.             clip_vector(x,y);
  1038.         else
  1039.         clip_move(x,y);
  1040.         }
  1041.     }
  1042. }
  1043.  
  1044. /* cntr3d_points:
  1045.  * Plot a surface contour in POINTS style
  1046.  */
  1047. static cntr3d_points(cntr, plot)
  1048.     struct gnuplot_contours *cntr;
  1049.     struct surface_points *plot;
  1050. {
  1051.     int i,j,k;
  1052.     int x,y;
  1053.     struct termentry *t = &term_tbl[term];
  1054.  
  1055.     if (draw_contour == CONTOUR_SRF || draw_contour == CONTOUR_BOTH) {
  1056.     for (i = 0; i < cntr->num_pts; i++) {
  1057.         if (real_z_max3d<cntr->coords[i].z)
  1058.         real_z_max3d=cntr->coords[i].z;
  1059.         if (real_z_min3d>cntr->coords[i].z)
  1060.         real_z_min3d=cntr->coords[i].z;
  1061.  
  1062.             map3d_xy(cntr->coords[i].x, cntr->coords[i].y, cntr->coords[i].z,
  1063.                  &x, &y);
  1064.  
  1065.         if (!clip_point(x,y))
  1066.         (*t->point)(x,y, plot->point_type);
  1067.         }
  1068.     }
  1069.  
  1070.     if (draw_contour == CONTOUR_BASE || draw_contour == CONTOUR_BOTH) {
  1071.     for (i = 0; i < cntr->num_pts; i++) {
  1072.         if (real_z_max3d<cntr->coords[i].z)
  1073.         real_z_max3d=cntr->coords[i].z;
  1074.         if (real_z_min3d>cntr->coords[i].z)
  1075.         real_z_min3d=cntr->coords[i].z;
  1076.  
  1077.             map3d_xy(cntr->coords[i].x, cntr->coords[i].y, z_min3d,
  1078.                  &x, &y);
  1079.  
  1080.         if (!clip_point(x,y))
  1081.         (*t->point)(x,y, plot->point_type);
  1082.         }
  1083.     }
  1084. }
  1085.  
  1086. /* cntr3d_dots:
  1087.  * Plot a surface contour in DOTS style
  1088.  */
  1089. static cntr3d_dots(cntr)
  1090.     struct gnuplot_contours *cntr;
  1091. {
  1092.     int i,j,k;
  1093.     int x,y;
  1094.     struct termentry *t = &term_tbl[term];
  1095.  
  1096.     if (draw_contour == CONTOUR_SRF || draw_contour == CONTOUR_BOTH) {
  1097.     for (i = 0; i < cntr->num_pts; i++) {
  1098.         if (real_z_max3d<cntr->coords[i].z)
  1099.         real_z_max3d=cntr->coords[i].z;
  1100.         if (real_z_min3d>cntr->coords[i].z)
  1101.         real_z_min3d=cntr->coords[i].z;
  1102.  
  1103.             map3d_xy(cntr->coords[i].x, cntr->coords[i].y, cntr->coords[i].z,
  1104.                  &x, &y);
  1105.  
  1106.         if (!clip_point(x,y))
  1107.         (*t->point)(x,y, -1);
  1108.         }
  1109.     }
  1110.  
  1111.     if (draw_contour == CONTOUR_BASE || draw_contour == CONTOUR_BOTH) {
  1112.     for (i = 0; i < cntr->num_pts; i++) {
  1113.         if (real_z_max3d<cntr->coords[i].z)
  1114.         real_z_max3d=cntr->coords[i].z;
  1115.         if (real_z_min3d>cntr->coords[i].z)
  1116.         real_z_min3d=cntr->coords[i].z;
  1117.  
  1118.             map3d_xy(cntr->coords[i].x, cntr->coords[i].y, z_min3d,
  1119.                  &x, &y);
  1120.  
  1121.         if (!clip_point(x,y))
  1122.         (*t->point)(x,y, -1);
  1123.         }
  1124.     }
  1125. }
  1126.  
  1127. static update_extrema_pts(ix, iy, min_sx_x, min_sx_y, min_sy_x, min_sy_y,
  1128.               x, y)
  1129.     int ix, iy, *min_sx_x, *min_sx_y, *min_sy_x, *min_sy_y;
  1130.     double x, y;
  1131. {
  1132.  
  1133.     if (*min_sx_x > ix + 2 ||         /* find (bottom) left corner of grid */
  1134.     (abs(*min_sx_x - ix) <= 2 && *min_sx_y > iy)) {
  1135.     *min_sx_x = ix;
  1136.     *min_sx_y = iy;
  1137.     min_sx_ox = x;
  1138.     min_sx_oy = y;
  1139.     }
  1140.     if (*min_sy_y > iy + 2 ||         /* find bottom (right) corner of grid */
  1141.     (abs(*min_sy_y - iy) <= 2 && *min_sy_x < ix)) {
  1142.     *min_sy_x = ix;
  1143.     *min_sy_y = iy;
  1144.     min_sy_ox = x;
  1145.     min_sy_oy = y;
  1146.     }
  1147. }
  1148.  
  1149. /* Draw the bottom grid for the parametric case. */
  1150. static draw_parametric_grid(plot)
  1151.     struct surface_points *plot;
  1152. {
  1153.     int i,ix,iy,            /* point in terminal coordinates */
  1154.     min_sx_x = 10000,min_sx_y = 10000,min_sy_x = 10000,min_sy_y = 10000,
  1155.         grid_iso = plot->plot_type == DATA3D && plot->has_grid_topology ?
  1156.                     plot->num_iso_read : iso_samples;
  1157.     double x,y,dx,dy;
  1158.     struct termentry *t = &term_tbl[term];
  1159.  
  1160.     if (grid && plot->has_grid_topology) {
  1161.         x = x_min3d;
  1162.         y = y_min3d;
  1163.  
  1164.     dx = (x_max3d-x_min3d) / (grid_iso-1);
  1165.     dy = (y_max3d-y_min3d) / (grid_iso-1);
  1166.  
  1167.     for (i = 0; i < grid_iso; i++) {
  1168.             if (i == 0 || i == grid_iso-1)
  1169.             (*t->linetype)(-2);
  1170.         else
  1171.             (*t->linetype)(-1);
  1172.         map3d_xy(x_min3d, y, z_min3d, &ix, &iy);
  1173.         clip_move(ix,iy);
  1174.         update_extrema_pts(ix,iy,&min_sx_x,&min_sx_y,
  1175.                    &min_sy_x,&min_sy_y,x_min3d,y);
  1176.  
  1177.         map3d_xy(x_max3d, y, z_min3d, &ix, &iy);
  1178.         clip_vector(ix,iy);
  1179.         update_extrema_pts(ix,iy,&min_sx_x,&min_sx_y,
  1180.                    &min_sy_x,&min_sy_y,x_max3d,y);
  1181.  
  1182.         y += dy;
  1183.     }
  1184.  
  1185.     for (i = 0; i < grid_iso; i++) {
  1186.             if (i == 0 || i == grid_iso-1)
  1187.             (*t->linetype)(-2);
  1188.         else
  1189.             (*t->linetype)(-1);
  1190.         map3d_xy(x, y_min3d, z_min3d, &ix, &iy);
  1191.         clip_move(ix,iy);
  1192.         update_extrema_pts(ix,iy,&min_sx_x,&min_sx_y,
  1193.                    &min_sy_x,&min_sy_y,x,y_min3d);
  1194.  
  1195.         map3d_xy(x, y_max3d, z_min3d, &ix, &iy);
  1196.         clip_vector(ix,iy);
  1197.         update_extrema_pts(ix,iy,&min_sx_x,&min_sx_y,
  1198.                    &min_sy_x,&min_sy_y,x,y_max3d);
  1199.  
  1200.         x += dx;
  1201.     }
  1202.     }
  1203.     else {
  1204.     (*t->linetype)(-2);
  1205.  
  1206.     map3d_xy(x_min3d, y_min3d, z_min3d, &ix, &iy);
  1207.     clip_move(ix,iy);
  1208.     update_extrema_pts(ix,iy,&min_sx_x,&min_sx_y,
  1209.                &min_sy_x,&min_sy_y,x_min3d,y_min3d);
  1210.  
  1211.     map3d_xy(x_max3d, y_min3d, z_min3d, &ix, &iy);
  1212.     clip_vector(ix,iy);
  1213.     update_extrema_pts(ix,iy,&min_sx_x,&min_sx_y,
  1214.                &min_sy_x,&min_sy_y,x_max3d,y_min3d);
  1215.  
  1216.     map3d_xy(x_max3d, y_max3d, z_min3d, &ix, &iy);
  1217.     clip_vector(ix,iy);
  1218.     update_extrema_pts(ix,iy,&min_sx_x,&min_sx_y,
  1219.                &min_sy_x,&min_sy_y,x_max3d,y_max3d);
  1220.  
  1221.     map3d_xy(x_min3d, y_max3d, z_min3d, &ix, &iy);
  1222.     clip_vector(ix,iy);
  1223.     update_extrema_pts(ix,iy,&min_sx_x,&min_sx_y,
  1224.                &min_sy_x,&min_sy_y,x_min3d,y_max3d);
  1225.  
  1226.  
  1227.     map3d_xy(x_min3d, y_min3d, z_min3d, &ix, &iy);
  1228.     clip_vector(ix,iy);
  1229.     }
  1230. }
  1231.  
  1232. /* Draw the bottom grid for non parametric case. */
  1233. static draw_non_param_grid(plot)
  1234.     struct surface_points *plot;
  1235. {
  1236.     int i,is_boundary=TRUE,crv_count=0,
  1237.     x,y,                /* point in terminal coordinates */
  1238.     min_sx_x = 10000,min_sx_y = 10000,min_sy_x = 10000,min_sy_y = 10000,
  1239.         grid_samples = plot->plot_type == DATA3D && plot->has_grid_topology ?
  1240.                     plot->iso_crvs->p_count : samples,
  1241.         grid_iso = plot->plot_type == DATA3D && plot->has_grid_topology ?
  1242.                     plot->num_iso_read : iso_samples;
  1243.     struct termentry *t = &term_tbl[term];
  1244.     struct iso_curve *icrvs = plot->iso_crvs;
  1245.  
  1246.     while ( icrvs ) {
  1247.     struct coordinate *points = icrvs->points;
  1248.  
  1249.     for (i = 0; i < icrvs->p_count; i += icrvs->p_count-1) {
  1250.         map3d_xy(points[i].x, points[i].y, z_min3d, &x, &y);
  1251.         if (is_boundary) {
  1252.         (*t->linetype)(-2);
  1253.         }
  1254.         else {
  1255.             (*t->linetype)(-1);
  1256.         }
  1257.  
  1258.         if (i > 0) {
  1259.             clip_vector(x,y);
  1260.         }
  1261.         else {
  1262.             clip_move(x,y);
  1263.         }
  1264.  
  1265.         if (draw_surface &&
  1266.             is_boundary &&
  1267.             (i == 0 || i == icrvs->p_count-1)) {
  1268.             int x1,y1;            /* point in terminal coordinates */
  1269.  
  1270.         /* Draw a vertical line to surface corner from grid corner. */
  1271.             map3d_xy(points[i].x, points[i].y, points[i].z, &x1, &y1);
  1272.             clip_vector(x1,y1);
  1273.             clip_move(x,y);
  1274.         update_extrema_pts(x,y,&min_sx_x,&min_sx_y, &min_sy_x,&min_sy_y,
  1275.                    points[i].x,points[i].y);
  1276.         }
  1277.     }
  1278.  
  1279.     if (grid) {
  1280.         crv_count++;
  1281.         icrvs = icrvs->next;
  1282.         is_boundary = crv_count == grid_iso - 1 ||
  1283.               crv_count == grid_iso ||
  1284.               (icrvs && icrvs->next == NULL);
  1285.     }
  1286.     else {
  1287.         switch (crv_count++) {
  1288.         case 0:
  1289.             for (i = 0; i < grid_iso - 1; i++)
  1290.             icrvs = icrvs->next;
  1291.             break;
  1292.         case 1:
  1293.             icrvs = icrvs->next;
  1294.             break;
  1295.         case 2:
  1296.             while (icrvs->next)
  1297.             icrvs = icrvs->next;
  1298.             break;
  1299.         case 3:
  1300.             icrvs = NULL;
  1301.             break;
  1302.         }
  1303.         }
  1304.     }
  1305. }
  1306.  
  1307. /* Draw the bottom grid that hold the tic marks for 3d surface. */
  1308. static draw_bottom_grid(plot, min_z, max_z)
  1309.     struct surface_points *plot;
  1310.     double min_z, max_z;
  1311. {
  1312.     int i,j,k;                /* point index */
  1313.     int x,y,min_x = 10000,min_y = 10000;/* point in terminal coordinates */
  1314.     double xtic,ytic,ztic;
  1315.     struct termentry *t = &term_tbl[term];
  1316.  
  1317.     xtic = make_3dtics(x_min3d,x_max3d,'x',log_x);
  1318.     ytic = make_3dtics(y_min3d,y_max3d,'y',log_y);
  1319.     ztic = make_3dtics(min_z,max_z,'z',log_z);
  1320.  
  1321.     if (draw_border)
  1322.     if (parametric || !plot->has_grid_topology)
  1323.         draw_parametric_grid(plot);
  1324.     else
  1325.         draw_non_param_grid(plot);
  1326.  
  1327.     (*t->linetype)(-2); /* border linetype */
  1328.  
  1329. /* label x axis tics */
  1330.     if (xtics && xtic > 0.0) {
  1331.         switch (xticdef.type) {
  1332.             case TIC_COMPUTED:
  1333.          if (x_min3d < x_max3d)
  1334.             draw_3dxtics(xtic * floor(x_min3d/xtic),
  1335.                      xtic,
  1336.                      xtic * ceil(x_max3d/xtic),
  1337.                      min_sy_oy);
  1338.                 else
  1339.             draw_3dxtics(xtic * floor(x_max3d/xtic),
  1340.                      xtic,
  1341.                      xtic * ceil(x_min3d/xtic),
  1342.                      min_sy_oy);
  1343.             break;
  1344.         case TIC_SERIES:
  1345.         draw_series_3dxtics(xticdef.def.series.start, 
  1346.                     xticdef.def.series.incr, 
  1347.                     xticdef.def.series.end,
  1348.                     min_sy_oy);
  1349.         break;
  1350.         case TIC_USER:
  1351.         draw_set_3dxtics(xticdef.def.user,
  1352.                  min_sy_oy);
  1353.         break;
  1354.             default:
  1355.             (*t->text)();
  1356.             (void) fflush(outfile);
  1357.             int_error("unknown tic type in xticdef in do_3dplot", NO_CARET);
  1358.             break;        /* NOTREACHED */
  1359.         }
  1360.     }
  1361. /* label y axis tics */
  1362.     if (ytics && ytic > 0.0) {
  1363.         switch (yticdef.type) {
  1364.             case TIC_COMPUTED:
  1365.          if (y_min3d < y_max3d)
  1366.             draw_3dytics(ytic * floor(y_min3d/ytic),
  1367.                      ytic,
  1368.                      ytic * ceil(y_max3d/ytic),
  1369.                      min_sy_ox);
  1370.                 else
  1371.             draw_3dytics(ytic * floor(y_max3d/ytic),
  1372.                      ytic,
  1373.                      ytic * ceil(y_min3d/ytic),
  1374.                      min_sy_ox);
  1375.             break;
  1376.         case TIC_SERIES:
  1377.         draw_series_3dytics(yticdef.def.series.start, 
  1378.                     yticdef.def.series.incr, 
  1379.                     yticdef.def.series.end,
  1380.                     min_sy_ox);
  1381.         break;
  1382.         case TIC_USER:
  1383.         draw_set_3dytics(yticdef.def.user,
  1384.                  min_sy_ox);
  1385.         break;
  1386.             default:
  1387.             (*t->text)();
  1388.             (void) fflush(outfile);
  1389.             int_error("unknown tic type in yticdef in do_3dplot", NO_CARET);
  1390.             break;        /* NOTREACHED */
  1391.         }
  1392.     }
  1393. /* label z axis tics */
  1394.     if (ztics && ztic > 0.0 && (draw_surface ||
  1395.                 draw_contour == CONTOUR_SRF ||
  1396.                 draw_contour == CONTOUR_BOTH)) {
  1397.         switch (zticdef.type) {
  1398.             case TIC_COMPUTED:
  1399.          if (min_z < max_z)
  1400.             draw_3dztics(ztic * floor(min_z/ztic),
  1401.                      ztic,
  1402.                      ztic * ceil(max_z/ztic),
  1403.                  min_sx_ox,
  1404.                      min_sx_oy,
  1405.                      min_z,
  1406.                  max_z);
  1407.                 else
  1408.             draw_3dztics(ztic * floor(max_z/ztic),
  1409.                      ztic,
  1410.                      ztic * ceil(min_z/ztic),
  1411.                      min_sx_ox,
  1412.                  min_sx_oy,
  1413.                      max_z,
  1414.                  min_z);
  1415.             break;
  1416.         case TIC_SERIES:
  1417.         draw_series_3dztics(zticdef.def.series.start, 
  1418.                     zticdef.def.series.incr, 
  1419.                     zticdef.def.series.end,
  1420.                     min_sx_ox,
  1421.                     min_sx_oy,
  1422.                     min_z,
  1423.                     max_z);
  1424.  
  1425.         break;
  1426.         case TIC_USER:
  1427.         draw_set_3dztics(zticdef.def.user,
  1428.                  min_sx_ox,
  1429.                      min_sx_oy,
  1430.                      min_z,
  1431.                  max_z);
  1432.         break;
  1433.             default:
  1434.             (*t->text)();
  1435.             (void) fflush(outfile);
  1436.             int_error("unknown tic type in zticdef in do_3dplot", NO_CARET);
  1437.             break;        /* NOTREACHED */
  1438.         }
  1439.     }
  1440.  
  1441. /* PLACE XLABEL - along the middle grid X axis */
  1442.     if (strlen(xlabel) > 0) {
  1443.        int x1,y1;
  1444.        double step = apx_eq( min_sy_oy, y_min3d ) ?    (y_max3d-y_min3d)/4
  1445.                               : (y_min3d-y_max3d)/4;
  1446.            map3d_xy((x_min3d+x_max3d)/2,min_sy_oy-step, z_min3d,&x1,&y1);
  1447.        x1 += xlabel_xoffset * t->h_char;
  1448.        y1 += xlabel_yoffset * t->v_char;
  1449.        if ((*t->justify_text)(CENTRE))
  1450.         clip_put_text(x1,y1,xlabel);
  1451.        else
  1452.         clip_put_text(x1 - strlen(xlabel)*(t->h_char)/2,y1,xlabel);
  1453.     }
  1454.  
  1455. /* PLACE YLABEL - along the middle grid Y axis */
  1456.     if (strlen(ylabel) > 0) {
  1457.        int x1,y1;
  1458.        double step = apx_eq( min_sy_ox, x_min3d ) ?    (x_max3d-x_min3d)/4
  1459.                               : (x_min3d-x_max3d)/4;
  1460.            map3d_xy(min_sy_ox-step,(y_min3d+y_max3d)/2,z_min3d,&x1,&y1);
  1461.        x1 += ylabel_xoffset * t->h_char;
  1462.        y1 += ylabel_yoffset * t->v_char;
  1463.        if ((*t->justify_text)(CENTRE))
  1464.         clip_put_text(x1,y1,ylabel);
  1465.        else
  1466.         clip_put_text(x1 - strlen(ylabel)*(t->h_char)/2,y1,ylabel);
  1467.     }
  1468.  
  1469. /* PLACE ZLABEL - along the middle grid Z axis */
  1470.     if (strlen(zlabel) > 0 &&
  1471.         (draw_surface ||
  1472.      draw_contour == CONTOUR_SRF ||
  1473.      draw_contour == CONTOUR_BOTH)) {
  1474.            map3d_xy(min_sx_ox,min_sx_oy,max_z + (max_z-min_z)/4, &x, &y);
  1475.  
  1476.        x += zlabel_xoffset * t->h_char;
  1477.        y += zlabel_yoffset * t->v_char;
  1478.        if ((*t->justify_text)(CENTRE))
  1479.         clip_put_text(x,y,zlabel);
  1480.        else
  1481.         clip_put_text(x - strlen(zlabel)*(t->h_char)/2,y,zlabel);
  1482.     }
  1483. }
  1484.  
  1485. /* DRAW_3DXTICS: draw a regular tic series, x axis */
  1486. static draw_3dxtics(start, incr, end, ypos)
  1487.     double start, incr, end, ypos; /* tic series definition */
  1488.         /* assume start < end, incr > 0 */
  1489. {
  1490.     double ticplace;
  1491.     int ltic;        /* for mini log tics */
  1492.     double lticplace;    /* for mini log tics */
  1493.  
  1494.     end = end + SIGNIF*incr; 
  1495.  
  1496.     for (ticplace = start; ticplace <= end; ticplace +=incr) {
  1497.         if (ticplace < x_min3d || ticplace > x_max3d) continue;
  1498.         xtick(ticplace, xformat, incr, 1.0, ypos);
  1499.         if (log_x && incr == 1.0) {
  1500.             /* add mini-ticks to log scale ticmarks */
  1501.             for (ltic = 2; ltic <= 9; ltic++) {
  1502.                 lticplace = ticplace+log10((double)ltic);
  1503.                 xtick(lticplace, "\0", incr, 0.5, ypos);
  1504.             }
  1505.         }
  1506.     }
  1507. }
  1508.  
  1509. /* DRAW_3DYTICS: draw a regular tic series, y axis */
  1510. static draw_3dytics(start, incr, end, xpos)
  1511.     double start, incr, end, xpos; /* tic series definition */
  1512.         /* assume start < end, incr > 0 */
  1513. {
  1514.     double ticplace;
  1515.     int ltic;        /* for mini log tics */
  1516.     double lticplace;    /* for mini log tics */
  1517.  
  1518.     end = end + SIGNIF*incr; 
  1519.  
  1520.     for (ticplace = start; ticplace <= end; ticplace +=incr) {
  1521.         if (ticplace < y_min3d || ticplace > y_max3d) continue;
  1522.         ytick(ticplace, yformat, incr, 1.0, xpos);
  1523.         if (log_y && incr == 1.0) {
  1524.             /* add mini-ticks to log scale ticmarks */
  1525.             for (ltic = 2; ltic <= 9; ltic++) {
  1526.                 lticplace = ticplace+log10((double)ltic);
  1527.                 ytick(lticplace, "\0", incr, 0.5, xpos);
  1528.             }
  1529.         }
  1530.     }
  1531. }
  1532.  
  1533. /* DRAW_3DZTICS: draw a regular tic series, z axis */
  1534. static draw_3dztics(start, incr, end, xpos, ypos, z_min, z_max)
  1535.     double start, incr, end, xpos, ypos, z_min, z_max;
  1536.         /* assume start < end, incr > 0 */
  1537. {
  1538.     int x, y;
  1539.     double ticplace;
  1540.     int ltic;        /* for mini log tics */
  1541.     double lticplace;    /* for mini log tics */
  1542.     register struct termentry *t = &term_tbl[term];
  1543.  
  1544.     end = end + SIGNIF*incr; 
  1545.  
  1546.     for (ticplace = start; ticplace <= end; ticplace +=incr) {
  1547.         if (ticplace < z_min || ticplace > z_max) continue;
  1548.  
  1549.         ztick(ticplace, zformat, incr, 1.0, xpos, ypos);
  1550.         if (log_z && incr == 1.0) {
  1551.             /* add mini-ticks to log scale ticmarks */
  1552.             for (ltic = 2; ltic <= 9; ltic++) {
  1553.                 lticplace = ticplace+log10((double)ltic);
  1554.                 ztick(lticplace, "\0", incr, 0.5, xpos, ypos);
  1555.             }
  1556.         }
  1557.     }
  1558.  
  1559.     /* Make sure the vertical line is fully drawn. */
  1560.     (*t->linetype)(-2);    /* axis line type */
  1561.  
  1562.     map3d_xy(xpos, ypos, z_min3d, &x, &y);
  1563.     clip_move(x,y);
  1564.     map3d_xy(xpos, ypos, min(end,z_max)+(log_z ? incr : 0.0), &x, &y);
  1565.     clip_vector(x,y);
  1566.  
  1567.     (*t->linetype)(-1); /* border linetype */
  1568. }
  1569.  
  1570. /* DRAW_SERIES_3DXTICS: draw a user tic series, x axis */
  1571. static draw_series_3dxtics(start, incr, end, ypos)
  1572.         double start, incr, end, ypos; /* tic series definition */
  1573.         /* assume start < end, incr > 0 */
  1574. {
  1575.     double ticplace, place;
  1576.     double ticmin, ticmax;    /* for checking if tic is almost inrange */
  1577.     double spacing = log_x ? log10(incr) : incr;
  1578.  
  1579.     if (end == VERYLARGE)
  1580.         end = max(CheckLog(log_x, x_min3d), CheckLog(log_x, x_max3d));
  1581.     else
  1582.       /* limit to right side of plot */
  1583.       end = min(end, max(CheckLog(log_x, x_min3d), CheckLog(log_x, x_max3d)));
  1584.  
  1585.     /* to allow for rounding errors */
  1586.     ticmin = min(x_min3d,x_max3d) - SIGNIF*incr;
  1587.     ticmax = max(x_min3d,x_max3d) + SIGNIF*incr;
  1588.     end = end + SIGNIF*incr; 
  1589.  
  1590.     for (ticplace = start; ticplace <= end; ticplace +=incr) {
  1591.         place = (log_x ? log10(ticplace) : ticplace);
  1592.         if ( inrange(place,ticmin,ticmax) )
  1593.          xtick(place, xformat, spacing, 1.0, ypos);
  1594.     }
  1595. }
  1596.  
  1597. /* DRAW_SERIES_3DYTICS: draw a user tic series, y axis */
  1598. static draw_series_3dytics(start, incr, end, xpos)
  1599.         double start, incr, end, xpos; /* tic series definition */
  1600.         /* assume start < end, incr > 0 */
  1601. {
  1602.     double ticplace, place;
  1603.     double ticmin, ticmax;    /* for checking if tic is almost inrange */
  1604.     double spacing = log_y ? log10(incr) : incr;
  1605.  
  1606.     if (end == VERYLARGE)
  1607.         end = max(CheckLog(log_y, y_min3d), CheckLog(log_y, y_max3d));
  1608.     else
  1609.       /* limit to right side of plot */
  1610.       end = min(end, max(CheckLog(log_y, y_min3d), CheckLog(log_y, y_max3d)));
  1611.  
  1612.     /* to allow for rounding errors */
  1613.     ticmin = min(y_min3d,y_max3d) - SIGNIF*incr;
  1614.     ticmax = max(y_min3d,y_max3d) + SIGNIF*incr;
  1615.     end = end + SIGNIF*incr; 
  1616.  
  1617.     for (ticplace = start; ticplace <= end; ticplace +=incr) {
  1618.         place = (log_y ? log10(ticplace) : ticplace);
  1619.         if ( inrange(place,ticmin,ticmax) )
  1620.          ytick(place, xformat, spacing, 1.0, xpos);
  1621.     }
  1622. }
  1623.  
  1624. /* DRAW_SERIES_3DZTICS: draw a user tic series, z axis */
  1625. static draw_series_3dztics(start, incr, end, xpos, ypos, z_min, z_max)
  1626.         double start, incr, end; /* tic series definition */
  1627.         double xpos, ypos, z_min, z_max;
  1628.         /* assume start < end, incr > 0 */
  1629. {
  1630.     int x, y;
  1631.     double ticplace, place;
  1632.     double ticmin, ticmax;    /* for checking if tic is almost inrange */
  1633.     double spacing = log_x ? log10(incr) : incr;
  1634.     register struct termentry *t = &term_tbl[term];
  1635.  
  1636.     if (end == VERYLARGE)
  1637.         end = max(CheckLog(log_z, z_min), CheckLog(log_z, z_max));
  1638.     else
  1639.       /* limit to right side of plot */
  1640.       end = min(end, max(CheckLog(log_z, z_min), CheckLog(log_z, z_max)));
  1641.  
  1642.     /* to allow for rounding errors */
  1643.     ticmin = min(z_min,z_max) - SIGNIF*incr;
  1644.     ticmax = max(z_min,z_max) + SIGNIF*incr;
  1645.     end = end + SIGNIF*incr; 
  1646.  
  1647.     for (ticplace = start; ticplace <= end; ticplace +=incr) {
  1648.         place = (log_z ? log10(ticplace) : ticplace);
  1649.         if ( inrange(place,ticmin,ticmax) )
  1650.          ztick(place, zformat, spacing, 1.0, xpos, ypos);
  1651.     }
  1652.  
  1653.     /* Make sure the vertical line is fully drawn. */
  1654.     (*t->linetype)(-2);    /* axis line type */
  1655.  
  1656.     map3d_xy(xpos, ypos, z_min3d, &x, &y);
  1657.     clip_move(x,y);
  1658.     map3d_xy(xpos, ypos, min(end,z_max)+(log_z ? incr : 0.0), &x, &y);
  1659.     clip_vector(x,y);
  1660.  
  1661.     (*t->linetype)(-1); /* border linetype */
  1662. }
  1663.  
  1664. /* DRAW_SET_3DXTICS: draw a user tic set, x axis */
  1665. static draw_set_3dxtics(list, ypos)
  1666.     struct ticmark *list;    /* list of tic marks */
  1667.     double ypos;
  1668. {
  1669.     double ticplace;
  1670.     double incr = (x_max3d - x_min3d) / 10;
  1671.     /* global x_min3d, x_max3d, xscale, y_min3d, y_max3d, yscale */
  1672.  
  1673.     while (list != NULL) {
  1674.        ticplace = (log_x ? log10(list->position) : list->position);
  1675.        if ( inrange(ticplace, x_min3d, x_max3d)         /* in range */
  1676.           || NearlyEqual(ticplace, x_min3d, incr)    /* == x_min */
  1677.           || NearlyEqual(ticplace, x_max3d, incr))    /* == x_max */
  1678.         xtick(ticplace, list->label, incr, 1.0, ypos);
  1679.  
  1680.        list = list->next;
  1681.     }
  1682. }
  1683.  
  1684. /* DRAW_SET_3DYTICS: draw a user tic set, y axis */
  1685. static draw_set_3dytics(list, xpos)
  1686.     struct ticmark *list;    /* list of tic marks */
  1687.     double xpos;
  1688. {
  1689.     double ticplace;
  1690.     double incr = (y_max3d - y_min3d) / 10;
  1691.     /* global x_min3d, x_max3d, xscale, y_min3d, y_max3d, yscale */
  1692.  
  1693.     while (list != NULL) {
  1694.        ticplace = (log_y ? log10(list->position) : list->position);
  1695.        if ( inrange(ticplace, y_min3d, y_max3d)           /* in range */
  1696.           || NearlyEqual(ticplace, y_min3d, incr)    /* == y_min3d */
  1697.           || NearlyEqual(ticplace, y_max3d, incr))    /* == y_max3d */
  1698.         ytick(ticplace, list->label, incr, 1.0, xpos);
  1699.  
  1700.        list = list->next;
  1701.     }
  1702. }
  1703.  
  1704. /* DRAW_SET_3DZTICS: draw a user tic set, z axis */
  1705. static draw_set_3dztics(list, xpos, ypos, z_min, z_max)
  1706.     struct ticmark *list;    /* list of tic marks */
  1707.     double xpos, ypos, z_min, z_max;
  1708. {
  1709.     int x, y;
  1710.     double ticplace;
  1711.     double incr = (z_max - z_min) / 10;
  1712.     register struct termentry *t = &term_tbl[term];
  1713.  
  1714.     while (list != NULL) {
  1715.        ticplace = (log_z ? log10(list->position) : list->position);
  1716.        if ( inrange(ticplace, z_min, z_max)         /* in range */
  1717.           || NearlyEqual(ticplace, z_min, incr)        /* == z_min */
  1718.           || NearlyEqual(ticplace, z_max, incr))    /* == z_max */
  1719.         ztick(ticplace, list->label, incr, 1.0, xpos, ypos);
  1720.  
  1721.        list = list->next;
  1722.     }
  1723.  
  1724.     /* Make sure the vertical line is fully drawn. */
  1725.     (*t->linetype)(-2);    /* axis line type */
  1726.  
  1727.     map3d_xy(xpos, ypos, z_min, &x, &y);
  1728.     clip_move(x,y);
  1729.     map3d_xy(xpos, ypos, z_max+(log_z ? incr : 0.0), &x, &y);
  1730.     clip_vector(x,y);
  1731.  
  1732.     (*t->linetype)(-1); /* border linetype */
  1733. }
  1734.  
  1735. /* draw and label a x-axis ticmark */
  1736. static xtick(place, text, spacing, ticscale, ypos)
  1737.         double place;                   /* where on axis to put it */
  1738.         char *text;                     /* optional text label */
  1739.         double spacing;         /* something to use with checkzero */
  1740.         double ticscale;         /* scale factor for tic mark (0..1] */
  1741.     double ypos;
  1742. {
  1743.     register struct termentry *t = &term_tbl[term];
  1744.     char ticlabel[101];
  1745.     int x0,y0,x1,y1,x2,y2,x3,y3;
  1746.     int ticsize = (int)((t->h_tic) * ticscale);
  1747.     double v[2], len;
  1748.  
  1749.     place = CheckZero(place,spacing); /* to fix rounding error near zero */
  1750.  
  1751.     if (place > x_max3d || place < x_min3d) return;
  1752.  
  1753.     map3d_xy(place, ypos, z_min3d, &x0, &y0);
  1754.     /* need to figure out which is in. pick the middle point along the */
  1755.     /* axis as in.                               */
  1756.     map3d_xy(place, (y_max3d + y_min3d) / 2, z_min3d, &x1, &y1);
  1757.  
  1758.     /* compute a vector of length 1 into the grid: */
  1759.     v[0] = x1 - x0;
  1760.     v[1] = y1 - y0;
  1761.     len = sqrt(v[0] * v[0] + v[1] * v[1]);
  1762.     v[0] /= len;
  1763.     v[1] /= len;
  1764.  
  1765.     if (tic_in) {
  1766.     x1 = x0;
  1767.     y1 = y0;
  1768.     x2 = x1 + ((int) (v[0] * ticsize));
  1769.     y2 = y1 + ((int) (v[1] * ticsize));
  1770.         x3 = x0 - ((int) (v[0] * ticsize * 3)); /* compute text position */
  1771.         y3 = y0 - ((int) (v[1] * ticsize * 3));
  1772.     } else {
  1773.     x1 = x0;
  1774.     y1 = y0;
  1775.     x2 = x0 - ((int) (v[0] * ticsize));
  1776.     y2 = y0 - ((int) (v[1] * ticsize));
  1777.         x3 = x0 - ((int) (v[0] * ticsize * 4)); /* compute text position */
  1778.         y3 = y0 - ((int) (v[1] * ticsize * 4));
  1779.     }
  1780.     clip_move(x1,y1);
  1781.     clip_vector(x2,y2);
  1782.  
  1783.     /* label the ticmark */
  1784.     if (text == NULL)
  1785.      text = xformat;
  1786.  
  1787.     (void) sprintf(ticlabel, text, CheckLog(log_x, place));
  1788.     if (apx_eq(v[0], 0.0)) {
  1789.         if ((*t->justify_text)(CENTRE)) {
  1790.             clip_put_text(x3,y3,ticlabel);
  1791.         } else {
  1792.             clip_put_text(x3-(t->h_char)*strlen(ticlabel)/2,y3,ticlabel);
  1793.         }
  1794.     }
  1795.     else if (v[0] > 0) {
  1796.         if ((*t->justify_text)(RIGHT)) {
  1797.             clip_put_text(x3,y3,ticlabel);
  1798.         } else {
  1799.             clip_put_text(x3-(t->h_char)*strlen(ticlabel),y3,ticlabel);
  1800.         }
  1801.     } else {
  1802.         (*t->justify_text)(LEFT);
  1803.     clip_put_text(x3,y3,ticlabel);
  1804.     }
  1805. }
  1806.  
  1807. /* draw and label a y-axis ticmark */
  1808. static ytick(place, text, spacing, ticscale, xpos)
  1809.         double place;                   /* where on axis to put it */
  1810.         char *text;                     /* optional text label */
  1811.         double spacing;         /* something to use with checkzero */
  1812.         double ticscale;         /* scale factor for tic mark (0..1] */
  1813.     double xpos;
  1814. {
  1815.     register struct termentry *t = &term_tbl[term];
  1816.     char ticlabel[101];
  1817.     int x0,y0,x1,y1,x2,y2,x3,y3;
  1818.     int ticsize = (int)((t->h_tic) * ticscale);
  1819.     double v[2], len;
  1820.  
  1821.     place = CheckZero(place,spacing); /* to fix rounding error near zero */
  1822.  
  1823.     if (place > y_max3d || place < y_min3d) return;
  1824.  
  1825.     map3d_xy(xpos, place, z_min3d, &x0, &y0);
  1826.     /* need to figure out which is in. pick the middle point along the */
  1827.     /* axis as in.                               */
  1828.     map3d_xy((x_max3d + x_min3d) / 2, place, z_min3d, &x1, &y1);
  1829.  
  1830.     /* compute a vector of length 1 into the grid: */
  1831.     v[0] = x1 - x0;
  1832.     v[1] = y1 - y0;
  1833.     len = sqrt(v[0] * v[0] + v[1] * v[1]);
  1834.     v[0] /= len;
  1835.     v[1] /= len;
  1836.  
  1837.     if (tic_in) {
  1838.     x1 = x0;
  1839.     y1 = y0;
  1840.     x2 = x1 + ((int) (v[0] * ticsize));
  1841.     y2 = y1 + ((int) (v[1] * ticsize));
  1842.         x3 = x0 - ((int) (v[0] * ticsize * 3)); /* compute text position */
  1843.         y3 = y0 - ((int) (v[1] * ticsize * 3));
  1844.     } else {
  1845.     x1 = x0;
  1846.     y1 = y0;
  1847.     x2 = x0 - ((int) (v[0] * ticsize));
  1848.     y2 = y0 - ((int) (v[1] * ticsize));
  1849.         x3 = x0 - ((int) (v[0] * ticsize * 4)); /* compute text position */
  1850.         y3 = y0 - ((int) (v[1] * ticsize * 4));
  1851.     }
  1852.     clip_move(x1,y1);
  1853.     clip_vector(x2,y2);
  1854.  
  1855.     /* label the ticmark */
  1856.     if (text == NULL)
  1857.      text = yformat;
  1858.  
  1859.     (void) sprintf(ticlabel, text, CheckLog(log_y, place));
  1860.     if (apx_eq(v[0], 0.0)) {
  1861.         if ((*t->justify_text)(CENTRE)) {
  1862.             clip_put_text(x3,y3,ticlabel);
  1863.         } else {
  1864.             clip_put_text(x3-(t->h_char)*strlen(ticlabel)/2,y3,ticlabel);
  1865.         }
  1866.     }
  1867.     else if (v[0] > 0) {
  1868.         if ((*t->justify_text)(RIGHT)) {
  1869.             clip_put_text(x3,y3,ticlabel);
  1870.         } else {
  1871.             clip_put_text(x3-(t->h_char)*strlen(ticlabel),y3,ticlabel);
  1872.         }
  1873.     } else {
  1874.         (*t->justify_text)(LEFT);
  1875.     clip_put_text(x3,y3,ticlabel);
  1876.     }
  1877. }
  1878.  
  1879. /* draw and label a z-axis ticmark */
  1880. static ztick(place, text, spacing, ticscale, xpos, ypos)
  1881.         double place;                   /* where on axis to put it */
  1882.         char *text;                     /* optional text label */
  1883.         double spacing;         /* something to use with checkzero */
  1884.         double ticscale;         /* scale factor for tic mark (0..1] */
  1885.     double xpos, ypos;
  1886. {
  1887.     register struct termentry *t = &term_tbl[term];
  1888.     char ticlabel[101];
  1889.     int x0,y0,x1,y1,x2,y2,x3,y3;
  1890.     int ticsize = (int)((t->h_tic) * ticscale);
  1891.  
  1892.     place = CheckZero(place,spacing); /* to fix rounding error near zero */
  1893.  
  1894.     map3d_xy(xpos, ypos, place, &x0, &y0);
  1895.  
  1896.     if (tic_in) {
  1897.     x1 = x0;
  1898.     y1 = y0;
  1899.     x2 = x0 + ticsize;
  1900.     y2 = y0;
  1901.         x3 = x0 - ticsize;
  1902.         y3 = y0;
  1903.     } else {
  1904.     x1 = x0;
  1905.     y1 = y0;
  1906.     x2 = x0 - ticsize;
  1907.     y2 = y0;
  1908.         x3 = x0 - ticsize * 2; /* compute text position */
  1909.         y3 = y0;
  1910.     }
  1911.     clip_move(x1,y1);
  1912.     clip_vector(x2,y2);
  1913.  
  1914.     /* label the ticmark */
  1915.     if (text == NULL)
  1916.      text = zformat;
  1917.  
  1918.     (void) sprintf(ticlabel, text, CheckLog(log_z, place));
  1919.     if ((*t->justify_text)(RIGHT)) {
  1920.         clip_put_text(x3,y3,ticlabel);
  1921.     } else {
  1922.         clip_put_text(x3-(t->h_char)*(strlen(ticlabel)+1),y3,ticlabel);
  1923.     }
  1924. }
  1925.